// Package threefish implements the Threefish tweakable block cipher.
// Threefish is designed to be the core function of the Skein hash function
// family.
// There are three versions of Threefish
//    - Threefish256  processes 256  bit blocks
//    - Threefish512  processes 512  bit blocks
//    - Threefish1024 processes 1024 bit blocks
package threefish

import (
    "errors"
    "crypto/cipher"
)

const (
    // The size of the tweak in bytes.
    TweakSize = 16
    // C240 is the key schedule constant
    C240 = 0x1bd11bdaa9fc1a22
    // The block size of Threefish-256 in bytes.
    BlockSize256 = 32
    // The block size of Threefish-512 in bytes.
    BlockSize512 = 64
    // The block size of Threefish-1024 in bytes.
    BlockSize1024 = 128
)

var errKeySize = errors.New("invalid key size")

// NewCipher returns a cipher.Block implementing the Threefish cipher.
// The length of the key must be 32, 64 or 128 byte.
// The length of the tweak must be TweakSize.
// The returned cipher implements:
//    - Threefish-256  - if len(key) = 32
//    - Threefish-512  - if len(key) = 64
//    - Threefish-1024 - if len(key) = 128
func NewCipher(tweak *[TweakSize]byte, key []byte) (cipher.Block, error) {
    switch k := len(key); k {
        default:
            return nil, errKeySize
        case BlockSize256:
            return newCipher256(tweak, key), nil
        case BlockSize512:
            return newCipher512(tweak, key), nil
        case BlockSize1024:
            return newCipher1024(tweak, key), nil
    }
}

// Increment the tweak by the ctr argument.
// Skein can consume messages up to 2^96 -1 bytes.
func IncrementTweak(tweak *[3]uint64, ctr uint64) {
    t0 := tweak[0]
    tweak[0] += ctr
    if tweak[0] < t0 {
        t1 := tweak[1]
        tweak[1] = (t1 + 1) & 0x00000000FFFFFFFF
    }
}

// The threefish-256 tweakable blockcipher
type threefish256 struct {
    keys  [5]uint64
    tweak [3]uint64
}

func (t *threefish256) BlockSize() int {
    return BlockSize256
}

// The threefish-512 tweakable blockcipher
type threefish512 struct {
    keys  [9]uint64
    tweak [3]uint64
}

func (t *threefish512) BlockSize() int {
    return BlockSize512
}

// The threefish-1024 tweakable blockcipher
type threefish1024 struct {
    keys  [17]uint64
    tweak [3]uint64
}

func (t *threefish1024) BlockSize() int {
    return BlockSize1024
}
